#include "mymap.h"
#include <fstream>
#include <QLineF>
#include <random>

#define SCALE_FACTOR 10  //初始绘制地图的缩放比例
#define NODE_SIZE 5    //绘制的节点大小
#define MAX_VISITINGNODE 10 //带参观景点队列的大小

void myNode::toJson(json& j) const {
    j["ID"] = m_ID;
    j["name"] = m_name;
    j["address"] = m_address;
    j["location"]["x"] = m_x * SCALE_FACTOR;
    j["location"]["y"] = m_y * SCALE_FACTOR;
    j["detail_info"]["tag"] = m_tag;
    j["detail_info"]["label"] = m_label;
    j["detail_info"]["overall_rating"] = m_rating;
    j["detail_info"]["comment_num"] = m_rating;
    j["detail_info"]["hot"] = m_hot;
    j["adjNode"] = m_adjNode;
}

void myNode::toJsonInner(json &j) const {
    j["ID"] = m_ID;
    j["name"] = m_name;
    j["x"] = m_x;
    j["y"] = m_y;
    j["adjNode"] = m_adjNode;
}
void myNode::fromJson(const json& j) {
    m_ID = j["ID"];
    m_name = j["name"];
    m_address = j["address"];
    m_x = j["location"]["x"];
    m_y = j["location"]["y"];
    m_x /= SCALE_FACTOR;
    m_y /= SCALE_FACTOR;
    m_tag = j["detail_info"]["tag"];
    m_label = j["detail_info"]["label"];
    m_rating = j["detail_info"]["overall_rating"];
    m_rateNum = j["detail_info"]["comment_num"];
    m_adjNode = j["adjNode"].get<std::vector<int>>();
}
void myNode::fromJsonInner(const json &j) {
    m_ID = j["ID"];
    m_name = j["name"];
    m_x = j["x"];
    m_y = j["y"];
    m_adjNode = j["adjNode"].get<std::vector<int>>();
}

int myNode::getID() const {
    return m_ID;
}
std::string myNode::getName() const {
    return m_name;
}
std::string myNode::getAddress() const {
    return m_address;
}
double myNode::getX() const {
    return m_x;
}
double myNode::getY() const {
    return m_y;
}
std::string myNode::getTag() const {
    return m_tag;
}
std::string myNode::getLabel() const {
    return m_label;
}
double myNode::getRating() const {
    return m_rating;
}
double myNode::getRateNum() const {
    return m_rateNum;
}
int myNode::getHot() const {
    return m_hot;
}

visibleNode *myNode::getPtr() const {
    return m_vptr;
}

std::vector<int> &myNode::getAdjNode() {
    return m_adjNode;
}

void myNode::setPtr(visibleNode *ptr) {
    m_vptr = ptr;
}

void myNode::addAdjNode(int node) {
    m_adjNode.push_back(node);
}

void myNode::init(int ID, std::string &name, double x, double y) {
    m_ID = ID;
    m_name = name;
    m_x = x;
    m_y = y;
}

void myNode::setID(const int ID) {
    m_ID = ID;
}


myMap::myMap(const int type, const std::string fileName)
    : m_type(type), m_fileName(fileName) {
    if (type != 2) {
        loadMap();
    }

}
myMap::~myMap() {
    saveMap();
}

std::vector<std::string>& myMap::getNameList() {
    return m_nameList;
}

std::unordered_map<int, myNode> &myMap::getAllNodeInfo() {
    return m_nodeList;
}

myNode& myMap::getNodeInfoName(const std::string &name) {
    return m_nodeList[m_nameToID[name]];
}

myNode &myMap::getNodeInfoID(const int ID) {
    return m_nodeList[ID];
}

std::vector<std::vector<bool> > &myMap::getPathInfo() {
    return m_pathMap;
}

void myMap::addVisitingNode(const int ID) {
    m_visitingNode.push_back(ID);
}

void myMap::removeVisitingNode(const int ID) {
    for (auto i = m_visitingNode.begin(); i != m_visitingNode.end(); ++i) {
        if (*i == ID) {
            m_visitingNode.erase(i);
            break;
        }
    }
}

int myMap::checkVisitingNode(const int ID) {
    if (m_visitingNode.size() == 0) {
        return 0;
    }
    else if (m_visitingNode.size() == MAX_VISITINGNODE) {
        return 1;
    }
    for (auto const& num : m_visitingNode) {
        if (num == ID) {
            return 2;
        }
    }
    return 3;
}

void myMap::generatePath() {
    std::mt19937 engine(time(nullptr));
    std::uniform_int_distribution<int> int_dist(1, 100);
    for (auto &node1 : m_nodeList) {
        for (auto &node2 : m_nodeList) {
            if (node1.first == node2.first)
                continue;
            QPointF p1(node1.second.getX(), node1.second.getY());
            QPointF p2(node2.second.getX(), node2.second.getY());
            qreal distance = QLineF(p1, p2).length();
            int random_int = int_dist(engine);
            if (distance < 50 && random_int < 100) {
                if (node1.first <= node2.first) {
                    node1.second.getAdjNode().push_back(node2.first);
                }
                m_pathMap[node1.first][node2.first] = true;
                m_pathMap[node2.first][node1.first] = true;
            }
        }
    }
    return;
}

void myMap::addNode(myNode* node) {
    m_nodeList.emplace(node->getID(), *node);
}

void myMap::addAdjNode(int ID, int adjNode) {
    m_nodeList[ID].addAdjNode(adjNode);
}
void myMap::loadMap() {
    std::ifstream file(m_fileName);
    if (!file.is_open()) {
        qDebug() << "地图信息文件打开失败";
        return;
    }
    json j;
    file >> j;
    m_nodeNum = j.size();
    m_pathMap.resize(m_nodeNum);
    for (auto& item : m_pathMap) {
        item.resize(m_nodeNum);
    }
    // int nodeNum = 0;
    for (const auto& item : j) {
        myNode node;
        if (m_type == FullType) {
            node.fromJson(item);
        } else {
            node.fromJsonInner(item);
        }
        // node.setID(nodeNum);     //调试用
        // ++nodeNum;
        for (auto& num : node.getAdjNode()) {
            m_pathMap[node.getID()][num] = true;
            m_pathMap[num][node.getID()] = true;
        }
        // node.getAdjNode().clear();   //调试用
        m_nameList.push_back(node.getName());
        m_nodeList.emplace(node.getID(), node);
        m_nameToID.emplace(node.getName(), node.getID());
    }
    qDebug() << "地图信息文件打开成功";
}
void myMap::saveMap() {
    json j;
    for (const auto& pair : m_nodeList) {
        json nodeJson;
        if (m_type == FullType) {
            pair.second.toJson(nodeJson);
        } else {
            pair.second.toJsonInner(nodeJson);
        }
        j.push_back(nodeJson);
    }

    std::ofstream file(m_fileName);
    file << j.dump(4);
    qDebug() << "地图信息文件保存成功";
}

void myMap::makePath(myNode *node) {
    if (nodePtr == NULL) {
        nodePtr = node;
    } else {
        if (node->getID() < nodePtr->getID()) {
            node->addAdjNode(nodePtr->getID());
        } else {
            nodePtr->addAdjNode(node->getID());
        }
        nodePtr = NULL;
    }
}

visibleCircle::visibleCircle(double size, QGraphicsItem *parent)
    : QGraphicsObject(parent), m_size(size) {
    setFlag(QGraphicsItem::ItemIsSelectable);   //设置节点为可选中
}

QRectF visibleCircle::boundingRect() const {
    return QRectF(-m_size/2, -m_size/2, m_size, m_size);
}

void visibleCircle::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) {
    painter->setBrush(isSelected() ? Qt::blue : Qt::gray);  //选中的节点设为蓝色，否则为灰色
    painter->drawEllipse(boundingRect());
}

void visibleCircle::mousePressEvent(QGraphicsSceneMouseEvent *event) {
    if (event->button() == Qt::LeftButton) {
        // emit makePath();
        setSelected(!isSelected());
        if (isSelected()) {
            emit selected();
        }
        event->accept();    //标记事件已处理，阻止事件传播（未显式调用默认为accept）
    } else {
        QGraphicsObject::mousePressEvent(event);    //事件传给父类处理
    }
}

visibleNode::visibleNode(qreal x, qreal y, qreal size, QObject *parent)
    :QObject(parent), m_x(x), m_y(y), m_size(size) {
    m_circle = new visibleCircle(size);
    m_circle->setPos(x, y);
    connect(m_circle, &visibleCircle::selected, this, [this]() {
        emit selected(m_nptr);
    });
    connect(m_circle, &visibleCircle::makePath, this, [this]() {
        emit makePath(m_nptr);
    });
}
visibleCircle *visibleNode::getCircle() const {
    return m_circle;
}
double visibleNode::getX() const {
    return m_x;
}
double visibleNode::getY() const {
    return m_y;
}
void visibleNode::setPtr(myNode *node) {
    m_nptr = node;
}

myNode *visibleNode::getPtr() const {
    return m_nptr;
}

visiblePath::visiblePath(myNode *p1, myNode *p2, QGraphicsItem *parent)
    : QGraphicsObject(parent), m_p1(QPointF(p1->getX(), p1->getY())), m_p2(QPointF(p2->getX(), p2->getY())) {
    //禁用交互
    setEnabled(false);
    setFlag(QGraphicsItem::ItemIsSelectable);
    setFlag(QGraphicsItem::ItemIsMovable);
    //定义线条的精准形状
    m_shape = QPainterPath();
    m_shape.moveTo(m_p1);
    m_shape.lineTo(m_p2);
    m_shape = m_shape.toReversed();
    m_shape.setFillRule(Qt::WindingFill);
    //定义线条的碰撞区域
    QPainterPathStroker stroker;
    stroker.setWidth(1);
    m_shape = stroker.createStroke(m_shape);
}

QRectF visiblePath::boundingRect() const {
    return m_shape.boundingRect();
}

QPainterPath visiblePath::shape() const {
    return m_shape;
}

void visiblePath::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) {
    painter->setPen(QPen(Qt::black, 0.5));
    painter->drawLine(m_p1, m_p2);
}

myView::myView(QWidget *parent)
    : QGraphicsView(parent){
    setScene(new QGraphicsScene(this));
    setRenderHint(QPainter::Antialiasing);      //设置抗锯齿
    setDragMode(QGraphicsView::NoDrag);         //禁用默认拖拽
    setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);   //禁用竖直滚动条
    setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);     //禁用横向滚动条
    scene()->clear();      //清除scene上的所有item
}

void myView::drawNode(myMap *map){
    for (auto& pair : map->getAllNodeInfo()) {
        visibleNode *node = new visibleNode(pair.second.getX(), pair.second.getY(), NODE_SIZE, this);
        scene()->addItem(node->getCircle());       //将node添加到scene中
        connect(node, &visibleNode::selected, this, [this, node]() {
            emit selected(node->getPtr());  //选中时发送信号，展示该节点信息
        });
        connect(node, &visibleNode::makePath, this, &myView::SmakePath);
        pair.second.setPtr(node);
        node->setPtr(&pair.second);
    }
}

void myView::drawPath(myMap *map) {
    std::vector<std::vector<bool>>& pathList = map->getPathInfo();
    int size = pathList.size();
    for (int i = 0; i < size; ++i) {
        for (int j = i + 1; j < size; ++j) {
            if (pathList[i][j] == 1) {
                visiblePath *path = new visiblePath(&map->getNodeInfoID(i), &map->getNodeInfoID(j));
                scene()->addItem(path);
            }
        }
    }
}

void myView::wheelEvent(QWheelEvent *event) {
    const double scaleFactor = 1.1;
    const double maxScale = 5.0;        //最大缩放倍率
    const double minScale = 0.5;        //最小缩放倍率
    double currentScale = transform().m11();    //当前缩放倍率，transform().m11()获取了当前视图的水平缩放因子
    if (event->angleDelta().y() > 0) {  //检测滚轮向上滚动
        if (currentScale < maxScale)
            scale(scaleFactor, scaleFactor);
    }
    else {  //滚轮向下滚动
        if (currentScale > minScale)
            scale(1/scaleFactor, 1/scaleFactor);
    }
}

void myView::mousePressEvent(QMouseEvent *event) {
    if (event->button() == Qt::LeftButton) {    //左键按下时
        QGraphicsItem *newNode = itemAt(event->pos());
        if (newNode != nullptr) {
            if (curNode != nullptr) {
                curNode->setSelected(false);
            }
            curNode = newNode;
            QGraphicsView::mousePressEvent(event);  //传递事件给场景
        }
        else {
            m_lastMousePos = event->pos();          //记录鼠标指针位置
            setCursor(Qt::ClosedHandCursor);        //将鼠标指针设置为拖拽手形
        }
    }
}

void myView::mouseMoveEvent(QMouseEvent *event) {
    //鼠标移动时不断计算指针的移动量并将横纵滚动条设置到相应的位置
    if (event->buttons() & Qt::LeftButton) {    //鼠标移动且左键按下
        if (!scene()->mouseGrabberItem()) {
            QPoint delta = event->pos() - m_lastMousePos;   //指针移动量
            QScrollBar *hBar = horizontalScrollBar();
            QScrollBar *vBar = verticalScrollBar();
            hBar->setValue(hBar->value() - delta.x());
            vBar->setValue(vBar->value() - delta.y());
            m_lastMousePos = event->pos();      //更新鼠标位置
        }
    }
    QGraphicsView::mouseMoveEvent(event); // 传递事件给场景
}

void myView::mouseReleaseEvent(QMouseEvent *event) {
    if (event->button() == Qt::LeftButton) {
        setCursor(Qt::ArrowCursor);     //左键释放时恢复指针
    }
    QGraphicsView::mouseReleaseEvent(event); // 传递事件给场景
}

void myView::SmakePath(myNode *node)
{
    emit makePath(node);
}





